package watcher

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/fsnotify/fsnotify"
	"github.com/micro-plat/lib4go/logger"
)

// FileWatcher 封装了监听文件所需的状态和行为
type FileWatcher struct {
	filePath        string
	file            *os.File
	reader          *bufio.Reader
	watcher         *fsnotify.Watcher
	mu              sync.Mutex // 用于保护文件读取的并发访问
	linesChan       chan string
	done            int32  // 使用原子操作来标记是否已完成
	savedOffset     int64  // 已保存的文件偏移量
	offset          int64  // 当前文件读取的偏移量
	offsetPath      string // 偏移量文件
	log             logger.ILogger
	ticker          *time.Ticker // 用于定时写入偏移量
	lineCount       int64        // 当前已读取的行数
	savedLinecoount int64
	watchDir        string
	needSaveOffset  bool // 是否需要保存偏移量
	doneChan        chan struct{}
	retryChan       chan struct{}
}

// offsetFileName 是记录偏移量的文件名，这里假设它与文件在同一目录下，以.offset为后缀
const offsetFileName = ".offset"

// NewFileWatcher 创建一个新的FileWatcher实例
func NewFileWatcher(filePath string, needSaveOffset bool) (*FileWatcher, error) {

	// 创建并初始化fsnotify实例
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	//创建FileWatcher实例
	fw := &FileWatcher{
		filePath:       filepath.Clean(filePath),
		watchDir:       filepath.Dir(filePath),
		watcher:        watcher,
		linesChan:      make(chan string, 10000),
		log:            logger.New("nginx-exporter"),
		doneChan:       make(chan struct{}),
		retryChan:      make(chan struct{}),
		ticker:         time.NewTicker(1 * time.Minute), // 定时1分钟
		needSaveOffset: needSaveOffset,
	}
	if needSaveOffset {
		// 加载偏移量文件
		fw.offsetPath, fw.offset = fw.loadOffsetFile()
		go fw.startOffsetWriter()
	}

	return fw, nil
}

// Start 开始监听文件的变化
func (w *FileWatcher) Start() {
	go func() {
		for {
			// 开始读取文件
			var err error
			w.file, err = w.loadFile(w.filePath, w.offset)
			if err != nil {
				w.log.Errorf("打开文件时出错:%s %v", w.filePath, err)
				time.Sleep(5 * time.Second)
				continue
			}

			// 创建读取器
			w.reader = bufio.NewReader(w.file)

			//读取已存在的内容
			w.readExistingLines()

			// 添加文件到监控器中
			if err := w.watcher.Add(w.watchDir); err != nil {
				w.log.Errorf("添加文件到监控器时出错:%s %v", w.watchDir, err)
				time.Sleep(5 * time.Second)
				continue
			}

			// 开始监听文件变化
			go w.watchFile()

			// 等待结束信号
			select {
			case <-w.retryChan: //
				w.log.Info("5秒后重新打开文件...")
				w.file.Close()
				time.Sleep(5 * time.Second)
			case <-w.doneChan: // 等待结束信号
				return

			}
		}
	}()

}

// 监听文件变化
func (w *FileWatcher) watchFile() {
	w.log.Info(fmt.Sprintf("正在监听目录:%s的变化", w.watchDir))

	for {
		select {
		case event, ok := <-w.watcher.Events:
			if !ok {
				w.log.Info("监控器关闭")
				return
			}
			// 判断是否是监听的文件
			if !strings.EqualFold(w.filePath, event.Name) {
				continue
			}

			if event.Op&fsnotify.Write == fsnotify.Write {
				// w.log.Infof("文件%s内容发生变化", w.filePath)
				w.readNewLines(true)
				continue
			}
			if event.Op&fsnotify.Rename == fsnotify.Rename ||
				event.Op&fsnotify.Remove == fsnotify.Remove {
				w.readNewLines(true) //读取剩下内容
				w.log.Errorf("文件%s被删除，重新打开", w.filePath)
				w.offset = 0
				w.writeOffset()
				w.retryChan <- struct{}{}
				return
			}

		case err, ok := <-w.watcher.Errors:
			if !ok {
				w.log.Info("监控器关闭")
				return
			}
			w.log.Errorf("监控器错误: %v", err)
		}
	}
}

// Subscribe 订阅通道，返回一个只读的通道供外部使用
func (w *FileWatcher) Subscribe() <-chan string {
	return w.linesChan
}

// Close 关闭文件和文件监控器，以及偏移量文件
func (w *FileWatcher) Close() {
	if atomic.CompareAndSwapInt32(&w.done, 0, 1) {
		w.mu.Lock()
		w.file.Close()
		w.watcher.Close()
		w.ticker.Stop()
		close(w.linesChan)
		close(w.doneChan)
		w.mu.Unlock()
		w.writeOffset()
	}
}
